﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Brain
{
    /// <summary>
    /// All the Basic Math Operations. Used by the Console
    /// </summary>
    public enum MathOperation
    {
        Addition, Subtraction, Multiplication, Division, None
    }
    public static class MathUtil
    {
        /// <summary>
        /// Returns the Hypotenuse of the Triangle
        /// </summary>
        /// <param name="Cathete1"></param>
        /// <param name="Cathete2"></param>
        /// <returns></returns>
        public static float Hypotenuse(float Cathete1, float Cathete2)
        {
            // h² = cat1² + cat2²
            float cat1 = Cathete1 * Cathete1;
            float cat2 = Cathete2 * Cathete2;
            float h = (float)Math.Sqrt(cat1 + cat2);
            return h;
        }

        /// <summary>
        /// Returns the Next Multiple of the Value
        /// </summary>
        /// <param name="value"></param>
        /// <param name="multiple"></param>
        /// <returns></returns>
        public static int NextMultiple(int value, int multiple)
        {
            //m = number
            //n = multiple
            //((m + n - 1) / n ) * n
            int val = Math.Abs(value);
            if (value == multiple ||
                value % multiple == 0)
                return value;
            if (value < 0)
            {
                return ((val + multiple - 1) / multiple) * -multiple;
            }
            else
            {
                return ((val + multiple - 1) / multiple) * multiple;
            }
        }

        /// <summary>
        /// Returns the Correct Math Operation
        /// </summary>
        /// <param name="ope"></param>
        /// <returns></returns>
        public static MathOperation GetOperation(string ope)
        {
            if (ope.Contains("+"))
                return MathOperation.Addition;
            if (ope.Contains("-"))
                return MathOperation.Subtraction;
            if (ope.Contains("*"))
                return MathOperation.Multiplication;
            if (ope.ToLower().Contains("x"))
                return MathOperation.Multiplication;
            if (ope.Contains("/"))
                return MathOperation.Division;

            return MathOperation.None;
        }

        /// <summary>
        /// Returns the Correct Math Operation
        /// </summary>
        /// <param name="ope"></param>
        /// <returns></returns>
        public static MathOperation GetOperation(ref string ope)
        {
            if (ope.Contains("+"))
            {
                ope = ope.Replace("+", "");
                return MathOperation.Addition;
            }
            if (ope.Contains("-"))
            {
                ope = ope.Replace("-", "");
                return MathOperation.Subtraction;
            }
            if (ope.Contains("*"))
            {
                ope = ope.Replace("*", "");
                return MathOperation.Multiplication;
            }
            if (ope.ToLower().Contains("x"))
            {
                ope = ope.Replace("x", "");
                return MathOperation.Multiplication;
            }
            if (ope.Contains("/"))
            {
                ope = ope.Replace("/", "");
                return MathOperation.Division;
            }

            return MathOperation.None;
        }

        /// <summary>
        /// Makes a MathOperation for this Number
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="number"></param>
        /// <returns></returns>
        public static int Operate(MathOperation operation, int numberOne, int numberTwo)
        {
            int final = numberOne;
            switch (operation)
            {
                case MathOperation.Addition:
                    {
                        final += numberTwo;
                    }
                    break;
                case MathOperation.Subtraction:
                    {
                        final -= numberTwo;
                    }
                    break;
                case MathOperation.Multiplication:
                    {
                        final *= numberTwo;
                    }
                    break;
                case MathOperation.Division:
                    {
                        final /= numberTwo;
                    }
                    break;
            }
            return final;
        }

        /// <summary>
        /// Returns a Fraction of a number. Example: Use 2/3 of the Screen Width.
        /// So call: Fraction(1280, 2, 3); that will return 853*
        /// </summary>
        /// <param name="value"></param>
        /// <param name="numerator"></param>
        /// <param name="denominator"></param>
        /// <returns></returns>
        public static float Fraction(float value, float numerator, float denominator)
        {
            return (value / denominator) * numerator;
        }

        public static float Percentage(float value, float Percentage)
        {
            float final = Percentage / 100;
            return value * final;
        }

        public static float Hermite(float start, float end, float value)
        {
            return FloatUtil.Lerp(start, end, value * value * (3.0f - 2.0f * value));
        }

        public static float Sinerp(float start, float end, float value)
        {
            return FloatUtil.Lerp(start, end, (float)Math.Sin(value * MathHelper.Pi * 0.5f));
        }

        public static float Coserp(float start, float end, float value)
        {
            return FloatUtil.Lerp(start, end, 1.0f - (float)Math.Cos(value * MathHelper.Pi * 0.5f));
        }

        public static float Berp(float start, float end, float value)
        {
            value = (float)(Math.Sin(value * Math.PI * (0.2f + 2.5f * value * value * value)) * Math.Pow(1f - value, 2.2f) + value) * (1f + (1.2f * (1f - value)));
            return start + (end - start) * value;
        }

        public static float SmoothStep(float x, float min, float max)
        {
            x = FloatUtil.Clamp(x, min, max);
            float v1 = (x - min) / (max - min);
            float v2 = (x - min) / (max - min);
            return -2 * v1 * v1 * v1 + 3 * v2 * v2;
        }

        public static float Lerp(float start, float end, float value)
        {
            return ((1.0f - value) * start) + (value * end);
        }

        
        public static float Bounce(float x)
        {
            return (float)Math.Abs(Math.Sin(6.28f * (x + 1f) * (x + 1f)) * (1f - x));
        }

        // test for value that is near specified float (due to floating point inprecision)
        // all thanks to Opless for this!
        public static bool Approx(float val, float about, float range)
        {
            return ((Math.Abs(val - about) < range));
        }

        /*
          * CLerp - Circular Lerp - is like lerp but handles the wraparound from 0 to 360.
          * This is useful when interpolating eulerAngles and the object
          * crosses the 0/360 boundary.  The standard Lerp function causes the object
          * to rotate in the wrong direction and looks stupid. Clerp fixes that.
          */
        public static float Clerp(float from, float to, float step)
        {
            float t = ((MathHelper.WrapAngle(to - from) * (step)));
            return from + t;
        } 

    }
}
